package fr.cephb.operon.server.servlet;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;



import fr.cephb.joperon.core.Operon;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.bio.Chromosome;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.PrjSeq;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.joperon.core.entities.SnpSeq;
import fr.cephb.joperon.tool.OperonFactory;

public class SNPServlet
	extends HttpServlet
	{
	private static final long serialVersionUID = 1L;
	private static final String RDF_NS= "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
	private static final String RDFS_NS= "http://www.w3.org/2000/01/rdf-schema#";
	
	private static final String NS= "http://ontology.cng.fr/snp#";
	private static final String PREFIX= "op";
	
	private Assembly assembly= Assembly.getDefaultAssembly();
	private File berkeleyDBHome=null;
	private long limitSnpCount=Long.MAX_VALUE;
	@Override
	public void init() throws ServletException
		{
		ServletContext ctx=getServletContext();
		String as=ctx.getInitParameter("assembly");
		if(as!=null)
			{
			this.assembly= Assembly.getAssemblyById(Integer.parseInt(as));
			}
		String db_home=ctx.getInitParameter("db_home");
		if(db_home==null)
			{
			db_home= Operon.guessBerkeleyDBHome();
			if(db_home==null) throw new ServletException("Cannot get BerkeleyDB home");
			
			}
		this.berkeleyDBHome= new File(db_home);
			
		
		String s= ctx.getInitParameter("limit");
		if(s!=null)
			{
			this.limitSnpCount= Long.parseLong(s);
			}
		}
	
	
	public Assembly getAssembly() {
		return assembly;
	}
	
	
	public File getBerkeleyDBHome() {
		return berkeleyDBHome;
		}
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException
		{
		
		Chromosome chrom=null;
		Integer start=null;
		Integer end=null;
		//parse parameters on URL
		try
			{
			String s= req.getParameter("chrom");
        	if(s!=null)
        		{
        		chrom= Chromosome.getByName(s);
        		if(chrom==Chromosome.ChrUndefined) chrom=null;
        		}
        	 s= req.getParameter("start");
            if(s!=null)
	    		{
	    		start=Integer.parseInt(s);
	    		}
            s= req.getParameter("end");
            if(s!=null)
	    		{
	    		end=Integer.parseInt(s);
	    		}
	        	
			}
		catch(Throwable err)//case of bad cast e.g. start=HELLO
			{
			chrom=null;
			start=null;
			end=null;
			}
       
        
        res.setContentType( "application/rdf+xml");
       
		
      
        // Get response body
        Operon operon=null;
        OperonFactory factory=null;
        OutputStream out= res.getOutputStream();
        XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
        XMLStreamWriter w=null;
        SnpMapDB.ValueIterator iter=null;
        try
        	{
        	operon= Operon.newInstance(getBerkeleyDBHome());
        	w=xmlfactory.createXMLStreamWriter(out);
        	w.writeStartDocument();
        	
        	w.writeStartElement("rdf", "RDF", RDF_NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdf", RDF_NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, PREFIX, NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdfs", RDFS_NS);
        	
        	w.writeComment("We use rdf ID based on LSID, but is not supported (fake lsid)");
        	if(chrom!=null && start!=null && end!=null && start< end)
        		{
        		factory= new OperonFactory(operon);
        		iter=factory.getSnpMap(getAssembly()).listBetween(chrom, start, end);
        		long snpIndex=0;
        		while(iter.hasNext())
        			{
        			SnpMap snp= iter.next();
        			if(!snp.getAcn().matches("rs[0-9]+")) continue;
        			
        			if(this.limitSnpCount<=snpIndex)
        				{
        				w.writeComment("Ouput limited to "+this.limitSnpCount+" entries");
        				}
        			snpIndex++;
        			
        			w.writeStartElement(PREFIX, "SnpMap", NS);
        			w.writeAttribute(RDF_NS, "about", "urn:lsid:cng.fr:SnpMap:"+snp.getFid().asString());
        			printElement(w,"name",snp.getAcn());
        			w.writeEmptyElement("rdfs", "seeAlso", RDFS_NS);
        			w.writeAttribute(RDF_NS, "resource", "http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?rs="+snp.getAcn().substring(2));
        			
        			
        			/** write position */
        			w.writeStartElement(PREFIX, "hasPosition", NS);
        			w.writeStartElement(PREFIX, "Fid", NS);
        			
        			w.writeEmptyElement(PREFIX, "assembly",NS);
        			w.writeAttribute(RDF_NS, "resource", "urn:lsid:cng.fr:Assembly:"+getAssembly().getShortCode());
        			
        			
        			w.writeEmptyElement(PREFIX, "chromosome", NS);
        			w.writeAttribute(RDF_NS, "resource", "urn:lsid:cng.fr:Chromosome:"+snp.getFid().getChromosome().toString());
        			printElement(w,"position",String.valueOf(snp.getFid().getPosition()));
        			printElement(w, "fid", snp.getFid().asString());
        			w.writeEndElement();
        			w.writeEndElement();
        			
        			/** write fasta */
        			SnpSeq snpseq= factory.getSnpSeq(getAssembly()).get(snp.getFid());
        			if(snpseq!=null)
        				{
        				w.writeStartElement(PREFIX, "hasFasta", NS);
            			w.writeCharacters( snpseq.asFastaSequence() );
            			w.writeEndElement();
        				}
        			/** write designs */
        			PrjSeq prjseq= factory.getPrjSeq(getAssembly()).get(snp.getFid());
        			if(prjseq==null)
        				{
        				w.writeComment("no design found for this SNP");
        				}
        			else
        				{
        				printElement(w,"hasTaqmanDesign",prjseq.asTaqmanRow());
        				printElement(w,"hasIlluminaDesign",prjseq.asIlluminaRow(getAssembly()));
        				printElement(w,"hasSnplexDesign",prjseq.asSnplexRow());
        				printElement(w,"hasSequenomDesign",prjseq.asSequenomRow());
        				}
        			
        			w.writeEndElement();
        			w.writeCharacters("\n");
        			}
        		}
        	w.writeEndElement();
        	w.writeEndDocument();
        	}
  
        catch (Exception e) {
        	e.printStackTrace();
			throw new IOException(e);
        	}
        finally
        	{
        	if(iter!=null) iter.close();
        	if(factory!=null) factory.close();
        	if(operon!=null) operon.safeClose();
        	if(w!=null )
        		{
        		try{
        			w.flush();
        			w.close();
        			}
        		catch (XMLStreamException e)
        			{
        			}
        		}
        	}
        out.flush();
        out.close();
		}
	
	private static void printElement(XMLStreamWriter w,String local,String text)
		throws XMLStreamException
		{
		w.writeStartElement(PREFIX, local, NS);
		w.writeCharacters(text);
		w.writeEndElement();
		}
	
	
	

}
